package io.gitee.zhangbinhub.acp.boot

import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator
import io.gitee.zhangbinhub.acp.boot.component.*
import io.gitee.zhangbinhub.acp.boot.conf.AcpTcpServerConfiguration
import io.gitee.zhangbinhub.acp.boot.conf.AcpUdpServerConfiguration
import io.gitee.zhangbinhub.acp.boot.exceptions.AcpDefaultWebExceptionHandler
import io.gitee.zhangbinhub.acp.boot.exceptions.AcpWebExceptionHandler
import io.gitee.zhangbinhub.acp.boot.init.AcpInitialization
import io.gitee.zhangbinhub.acp.boot.init.SystemInitialization
import io.gitee.zhangbinhub.acp.boot.listener.AcpListener
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.boot.socket.InitTcpServer
import io.gitee.zhangbinhub.acp.boot.socket.InitUdpServer
import io.gitee.zhangbinhub.acp.boot.socket.base.TcpServerHandle
import io.gitee.zhangbinhub.acp.boot.socket.base.UdpServerHandle
import io.gitee.zhangbinhub.acp.boot.tools.ServerTools
import io.gitee.zhangbinhub.acp.boot.tools.SpringBeanFactory
import io.netty.handler.codec.ByteToMessageDecoder
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter

/**
 * @since JDK 17
 */
@AutoConfiguration(
    after = [JacksonAutoConfiguration::class, AcpBootLogAutoConfiguration::class, AcpPropertiesAutoConfiguration::class],
    before = [HttpMessageConvertersAutoConfiguration::class]
)
class AcpComponentAutoConfiguration {
    @Bean
    fun systemInitialization(
        logAdapter: LogAdapter,
        serverTools: ServerTools,
        acpListenerList: MutableList<AcpListener>,
        acpTimerTaskSchedulerCtrl: AcpTimerTaskSchedulerCtrl,
        acpTcpServerConfiguration: AcpTcpServerConfiguration,
        acpUdpServerConfiguration: AcpUdpServerConfiguration,
        tcpServerHandle: MutableMap<String, TcpServerHandle>,
        udpServerHandle: MutableMap<String, UdpServerHandle>,
        @Autowired(required = false) byteToMessageDecoder: MutableMap<String, ByteToMessageDecoder>?
    ): AcpInitialization = SystemInitialization(
        logAdapter, serverTools, SystemControl(
            logAdapter,
            acpListenerList,
            acpTimerTaskSchedulerCtrl
        ),
        InitTcpServer(logAdapter, acpTcpServerConfiguration, tcpServerHandle, byteToMessageDecoder),
        InitUdpServer(logAdapter, acpUdpServerConfiguration, udpServerHandle)
    )

    @Bean
    @ConditionalOnMissingBean(SpringBeanFactory::class)
    fun springBeanFactory() = SpringBeanFactory()

    @Bean
    @ConditionalOnMissingBean(XmlMapper::class)
    fun jacksonXmlMapper(builder: Jackson2ObjectMapperBuilder): XmlMapper =
        builder.createXmlMapper(true).build<XmlMapper>().apply {
            this.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
        }

    @Bean
    @ConditionalOnMissingBean(MappingJackson2XmlHttpMessageConverter::class)
    fun mappingJackson2XmlHttpMessageConverter(builder: Jackson2ObjectMapperBuilder): MappingJackson2XmlHttpMessageConverter =
        MappingJackson2XmlHttpMessageConverter(jacksonXmlMapper(builder))

    @Bean
    fun fileDownLoadHandle(logAdapter: LogAdapter) = FileDownLoadHandle(logAdapter)

    @Bean
    fun defaultWebExceptionHandler(): AcpWebExceptionHandler = AcpDefaultWebExceptionHandler()

    @Bean
    fun serverTools() = ServerTools()

    @Bean
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    fun sessionWebFilter(): ServletWebFilter = ServletWebFilter()

    @Bean
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    fun reactiveWebFilter(): ReactiveWebFilter = ReactiveWebFilter()
}